﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the API Concepts Framework
 * http://apiconceptsframework.codeplex.com
 *
 * Copyright (C) 2014 eva Kühn, Thomas Scheller, Vienna University of Technology
 * http://www.complang.tuwien.ac.at/eva
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ApiConceptsFramework.Contract;
using ApiConceptsFramework.Concepts;

namespace ApiConceptsFramework.HCs
{
	public class ClassSwitches : IHighLevelConcept
	{
		public const int SwitchesPerAdditionalClass = 2;
		public const float ComplexityPerSwitch = 2.5f;
		public const float SwitchMultiplierForNewConcepts = 2.5f;
		public const float MinPercentOfNewConcepts = 0.2f;

		public HcEvaluationResult Evaluate(List<Assembly> apiAssemblies, IEnumerable<ConceptInstance> data, IHcEvaluationInput hcInputData, IEnumerable<HcEvaluationResult> otherHcData, IDE ide = IDE.VisualStudioOrSimilar)
		{
			if (hcInputData == null)
				return null;
			var pd = hcInputData as ClassSwitchesInputData;
			if (pd == null)
				throw new ArgumentException("hcInputData must be ClassSwitchesInputData", "hcInputData");
			
			if (pd.NumberOfClasses < 2)
				return null;

			var percentNewConcepts = CalculatePercentageOfNewConcepts(data);
			return GenerateResult(pd.NumberOfClasses, percentNewConcepts);
		}

		private HcEvaluationResult GenerateResult(int numberOfClasses, float percentNewConcepts)
		{
			var result = new HcEvaluationResult{HighLevelConcept = this};
			if (numberOfClasses > 0)
			{
				result.GeneralComplexity = CalculateComplexityFromNumberOfClasses(numberOfClasses, percentNewConcepts);
				result.GeneralDescription = "Complexity for switching between " + numberOfClasses + " implementation classes (" +
				                            (int) (percentNewConcepts*100) + "% new concepts)";
			}
			return result;
		}

		private float CalculateComplexityFromNumberOfClasses(int numberOfClasses, float percentNewConcepts)
		{
			float complexity = (numberOfClasses - 1) * SwitchesPerAdditionalClass * ComplexityPerSwitch;
			if (percentNewConcepts > MinPercentOfNewConcepts)
				complexity *= SwitchMultiplierForNewConcepts;
			return complexity;
		}

		private float CalculatePercentageOfNewConcepts(IEnumerable<ConceptInstance> data)
		{
			float numConcepts = data.Sum(c => WeightOfNewConcept(c));
			float numNewConcepts = data.Where(c => c.PreviousUsages == 0).Sum(c => WeightOfNewConcept(c));
			return numNewConcepts/numConcepts;
		}

		private int WeightOfNewConcept(ConceptInstance c)
		{
			if (c.Concept is ClassUsage)
				return 4;
			if (c.Concept is Instantiation)
				return 2;
			if (c.Concept is MethodCall)
				return 2;
			//if (c.Concept is FieldAccess)
			return 1;
		}

		public Type InputDataType
		{
			get { return typeof(ClassSwitchesInputData); }
		}
	}

	public class ClassSwitchesInputData : IHcEvaluationInput
	{
		public int NumberOfClasses { get; set; }
	}
}